import React, { Component } from 'react';
import './drawing-canvas.scss';
import pencil from '../../assets/brush/pencil_000.png';

class DrawingCanvas extends Component {
    constructor(props) {
        super(props);
    }

    canvas;
    context;
    fontColor = '#000';
    fontWeight = '4';
    eraserWeight = '10';
    img = new Image();
    pattern;
    lastPoint = { x: undefined, y: undefined }; // 记录最后一个点
    points = [];

    BRUSH_TYPE = {
        ERASER: 'STROKE_EDITING_ERASER',
        PEN: 'INKPEN',
        PENCIL: 'PENCIL',
    };

    /**
     * 鼠标点下事件
     */
    handleClick = e => {
        if (!this.isInDrawBoard(e)) {
            return;
        }
        const { x, y } = this.getMousePosition(e);
        this.context.beginPath();
        switch (this.props.brushType) {
            case this.BRUSH_TYPE.ERASER:
                this.pattern = null;
                this.clearCircle({ x, y });
                break;
            case this.BRUSH_TYPE.PENCIL:
                this.img.src = pencil;
                this.img.onload = () => {
                    const patternCanvas = document.createElement('canvas');
                    const ctx = patternCanvas.getContext('2d');
                    patternCanvas.width = 512;
                    patternCanvas.height = 512;
                    ctx.fillStyle = this.props.fontColor;
                    ctx.fillRect(
                        0,
                        0,
                        patternCanvas.width,
                        patternCanvas.height
                    );
                    ctx.drawImage(this.img, 0, 0);
                    this.pattern = this.context.createPattern(
                        patternCanvas,
                        'repeat'
                    );
                };
                break;
            default:
                this.pattern = null;
                this.drawCircle({ x, y });
                this.context.fillStyle = this.props.fontColor;
                this.context.fill();
                break;
        }
        this.points.push({ x, y });
        this.lastPoint = { x, y };
        document.documentElement.addEventListener(
            'mousemove',
            this.handleMouseMove
        );
    };

    handleMouseMove = e => {
        const { x, y } = this.getMousePosition(e);
        this.points.push({ x, y });

        if (this.points.length > 3) {
            const lastTwoPoints = this.points.slice(-2);
            const controlPoint = lastTwoPoints[0];
            const endPoint = {
                x: (lastTwoPoints[0].x + lastTwoPoints[1].x) / 2,
                y: (lastTwoPoints[0].y + lastTwoPoints[1].y) / 2,
            };
            if (this.props.brushType === this.BRUSH_TYPE.ERASER) {
                this.clearLine(controlPoint, endPoint);
            } else {
                this.drawLine(controlPoint, endPoint);
            }
            this.lastPoint = endPoint;
        }
    };

    handleMouseUp = e => {
        document.documentElement.removeEventListener(
            'mousemove',
            this.handleMouseMove
        );

        const { x, y } = this.getMousePosition(e);
        this.points.push({ x, y });
        if (this.points.length > 3) {
            const lastTwoPoints = this.points.slice(-2);
            const controlPoint = lastTwoPoints[0];
            const endPoint = {
                x: (lastTwoPoints[0].x + lastTwoPoints[1].x) / 2,
                y: (lastTwoPoints[0].y + lastTwoPoints[1].y) / 2,
            };
            if (this.props.brushType === this.BRUSH_TYPE.ERASER) {
                this.clearLine(controlPoint, endPoint);
            } else {
                this.drawLine(controlPoint, endPoint);
            }
        }
        this.lastPoint = { x: undefined, y: undefined };
        this.points = [];
    };

    render() {
        const svgCircle = (
            <svg
                class='icon'
                viewBox='0 0 1024 1024'
                xmlns='http://www.w3.org/2000/svg'
                width={this.fontWeight}
                height={this.fontWeight}
            >
                <path
                    d='M480 480m-160 0a2.5 2.5 0 1 0 320 0 2.5 2.5 0 1 0-320 0Z'
                    fill={this.props.fontColor}
                />
            </svg>
        );

        const cursorStyle = `url(data:image/svg+xml;utf8,${svgCircle}), auto`;
        return (
            <div
                className='drawing-canvas-container'
                style={{ cursor: cursorStyle }}
            >
                <canvas id='drawing-canvas' ref='canvasContainer' />
            </div>
        );
    }

    /**
     * 组件已经被渲染到 dom 后运行
     */
    componentDidMount() {
        this.beforeDraw();
        this.canvas.width = document.documentElement.clientWidth;
        this.canvas.height = document.documentElement.clientHeight;
    }

    componentDidUpdate() {
        this.beforeDraw();
    }

    /**
     * 绘画前准备
     */
    beforeDraw() {
        // 设置画板大小
        this.canvas = this.refs.canvasContainer;
        this.context = this.canvas.getContext('2d');

        // 绑定鼠标事件，在document绑定是为了能够监听得到它超出边缘后的事件
        document.documentElement.addEventListener(
            'mousedown',
            this.handleClick
        );
        document.documentElement.addEventListener(
            'mouseup',
            this.handleMouseUp
        );
    }

    /**
     * 组件销毁
     */
    componentWillUnmount() {
        document.documentElement.removeEventListener(
            'mousedown',
            this.handleClick
        );
        document.documentElement.removeEventListener(
            'mouseup',
            this.handleMouseUp
        );
    }

    /**
     * 获取鼠标在 canvas 的位置 {x, y}
     */
    getMousePosition = e => {
        const rect = this.canvas.getBoundingClientRect();
        const x = e.clientX - rect.left * (this.canvas.width / rect.width);
        const y = e.clientY - rect.top * (this.canvas.width / rect.width);
        return { x, y };
    };

    /**
     * 画圆
     * @param {*} x 横座标
     * @param {*} y 纵座标
     */
    drawCircle(x, y) {
        this.context.arc(x, y, this.fontWeight / 2, 0, Math.PI * 2, true);
    }

    /**
     * 绘制贝塞尔曲线，使线条更平滑
     * @param {*} controlPoint 控制点
     * @param {*} endPoint 终点
     */
    drawLine(controlPoint, endPoint) {
        this.context.beginPath();
        this.context.moveTo(this.lastPoint.x, this.lastPoint.y);
        this.context.quadraticCurveTo(
            controlPoint.x,
            controlPoint.y,
            endPoint.x,
            endPoint.y
        );
        this.context.lineWidth = this.fontWeight;
        this.context.lineCap = 'round';
        this.context.lineJoin = 'round';
        this.context.strokeStyle = this.props.fontColor;
        if (this.pattern) {
            this.context.save();
            this.context.strokeStyle = this.pattern;
            this.context.stroke();
            this.context.restore();
        } else {
            this.context.stroke();
        }
    }

    /**
     * 清除线
     * @param {*} controlPoint 控制点
     * @param {*} endPoint 终点
     */
    clearLine(controlPoint, endPoint) {
        this.context.save(); // 保存状态
        this.context.globalCompositeOperation = 'destination-out';
        this.context.beginPath();
        this.context.moveTo(this.lastPoint.x, this.lastPoint.y);
        this.context.quadraticCurveTo(
            controlPoint.x,
            controlPoint.y,
            endPoint.x,
            endPoint.y
        );
        this.context.lineWidth = this.eraserWeight;
        this.context.stroke();
        this.context.restore(); // 恢复先前的状态
    }

    /**
     * 清除圆点
     * @param {*} point 圆点
     */
    clearCircle(point) {
        this.context.save();
        this.context.globalCompositeOperation = 'destination-out';
        this.context.arc(
            point.x,
            point.y,
            this.eraserWeight / 2,
            0,
            2 * Math.PI,
            false
        );
        this.context.fill();
        this.context.restore();
    }

    isInDrawBoard(e) {
        const nodeName = e.target ? e.target.nodeName : e.srcElement.nodeName;
        return nodeName === 'CANVAS';
    }
}

export default DrawingCanvas;
